require "rubygems"
require "highline/import"
require 'pp' 
#require 'logger'
#require 'benchmark'
   
#@@logger = Logger.new(STDOUT)
#@@logger.level = Logger::DEBUG
  
class Card
  RANKS = [*3..35]
  
  attr_accessor :rank

  def initialize(id)
    self.rank = RANKS[id % 36]  
  end
end

class Deck
  attr_accessor :cards
  def initialize
    # shuffle array and init each Card
    self.cards = (0..32).to_a.shuffle.collect { |id| Card.new(id) }
  end
end

class Rules_NoThanks
	attr_accessor :pot, :turn, :j, :verbose
    attr_reader :howManyGames, :nbPlayers, :joueurs
    
    MAXTICKS = 300
    MAXTURNS = 24 # 24 tours dans une partie
    PASSE   = false
    PRENDRE = true
    

	def initialize(nbPlayers=4)
        @nbPlayers = nbPlayers	
        @pot, @turn, @j = 0, 0, 0
        @deck = Deck.new
        @ticks = 0
	end
	
	def start(howManyGames=1)
        @howManyGames = howManyGames
                
        if howManyGames == 1
              @joueurs = [  #( rules, AI, Verbose=true)
                Joueur.new(self, AI_NoThanks_Patoo, true),
                Joueur.new(self, AI_NoThanks_Humain, true),
                Joueur.new(self, AI_NoThanks_Colovini, true)
            ]
            if @nbPlayers >= 4
                @joueurs.push Joueur.new(self, AI_NoThanks_Feld, true)
            end
            
            if @nbPlayers == 5
                @joueurs.push Joueur.new(self, AI_NoThanks_Bruno, true)
            end
            @joueurs.shuffle!
            @currentPlayer = @joueurs[0]
                        
            @verbose = true
            
            annoncerCarte #annonce la 1ere carte.
            
            #loop sur l'action ici.
            until @turn == MAXTURNS or @ticks > MAXTICKS # 24 tours dans une partie
                @ticks += 1 # surtout pour prevenir infinite loop :p
                
                #puts "-=- J#{@j+1} argent: #{@currentPlayer.money}$ Tableau: #{@currentPlayer.tableau}  Score: #{@currentPlayer.score} -=- T#{@turn+1}"                
                #puts "#{@pot}$ + # #{@deck.cards[@turn].rank } => J#{@j+1}"
                
                action( @currentPlayer.play( @deck.cards[@turn].rank, @pot, MAXTURNS-turn ) )
                
                if @currentPlayer.money < 0 then 
                    puts "**** Houston on a un probleme. #{@currentPlayer.money} ****"
                end
            end
                        
            puts "=========== GAME OVER ==========="
        
            @nbPlayers.times do |j|
                puts "-=- j#{j+1} #{format('%3s',@joueurs[j].score)} pts, #{@joueurs[j].money}$ Ai:#{@joueurs[j].nom} Tableau: #{@joueurs[j].tableau}"
            end             
                        
            if @ticks > MAXTICKS then 
                puts "**** max ticks atteint. da fuq? ****" 
            else
                dit "Cartes manquantes #{extraCards}"
                winner                
                dit "@game ticks = #{@ticks}"s
            end
            
        else
        ######################
        ## Simulation Start ##
        ######################
            @verbose = false
            @joueurs = [  #( rules, AI, Verbose=true)
                Joueur.new(self, AI_NoThanks_Patoo, @verbose),
                Joueur.new(self, AI_NoThanks_Bruno, @verbose),
                Joueur.new(self, AI_NoThanks_Colovini, @verbose)
            ]
            if @nbPlayers >= 4
                @joueurs.push Joueur.new(self, AI_NoThanks_Feld, @verbose)
            end
            
            if @nbPlayers == 5
                @joueurs.push Joueur.new(self, AI_NoThanks_Bruno, @verbose)
            end
            @joueurs.shuffle!
            @currentPlayer = @joueurs[0]            
            
            puts "Simulation started.."
            
            howManyGames.times do |x|
                
                until @turn == MAXTURNS #24
                    action( @currentPlayer.play( @deck.cards[@turn].rank, @pot, MAXTURNS-turn ) )
                end
                @turn = 0
                #puts "** Game # #{x+1} Finished **"
              
                #inc = 
                #puts "#{inc-1}"
                #zz = gamesWon[inc]
                #puts "zz=#{zz}"
                                
                winner.winCount += 1 #garde le compte des victoires.
                
                # Assert que ya pas de infinite loop en cours.
                if @ticks > MAXTICKS 
                    puts "**** max ticks atteint. da fuq? ****" 
                end

                #RESET GAME
                @ticks = 0
                @deck.cards.shuffle!
                @joueurs.each {|j|  j.reset}
                @joueurs.shuffle!
                
            end
            
            ### Simulation Results here
            puts ">>> Simulation results after #{howManyGames} games <<<<<<<<<<<<<<<<"
            
            #sort la list par nom.
            @joueurs.sort!{ |a,b| a.winCount <=> b.winCount }.reverse!
            
            @joueurs.each do |x|                
                puts "#{format('%2d', x.winCount.to_f / howManyGames * 100)}% #{format('%3d',x.winCount)} #{x.nom} "
            end
        end
	end
	
    def annoncerCarte
        puts
        dit "=====#{MAXTURNS-turn}=======Offering card ##{@deck.cards[@turn].rank } ================="            
    end
    
    def showTableaux
        if howManyGames == 1
            allo = []
            @joueurs.each do |x|
                allo.push "#{x.nom} #{x.tableau}"
            end
            puts allo.to_s
        end
    end
    
    def getSeat(player)
        @joueurs.index(player)+1
    end
    
    def action(choix)

		if choix == PASSE
			dit "#{@currentPlayer.nom} passe."
            #puts "increasing pot"
			@pot += 1
			@j = (@j+1) % @nbPlayers # Change de joueur.
			@currentPlayer = @joueurs[j]
            
			#puts '===============NEW PLAYER================='
		
        
        else
        # ici le loop sur la carte.
        # prendre la carte et la mise.
            newCarte
		end	
    end
    
    def newCarte
        dit "#{@currentPlayer.nom} prend."            
        dit "j#{@j+1} <-- #{@pot}$ + [#{@deck.cards[@turn].rank}]   #{@currentPlayer.tableau}"
                    
        @pot = 0  #reset le 
        @turn +=1 #tourne prochaine carte.	
                    
        
        #puts "resetting pot"
        
        if @turn < MAXTURNS
            dit " "
            showTableaux
            annoncerCarte            
        end
    end
    
    def winner
        highScore = 999
        gagnant   = nil
        

        @joueurs.each do |j|    
            #pp j.score
            if ( j.score < highScore )
                highScore = j.score
                gagnant = j
               
            end            
        end

        dit "Le gagnant est #{gagnant.nom} j#{gagnant.seatPosition}"
        gagnant
    end
    
    def extraCards        
        
        allo = @deck.cards[24..33]
        
        extra=[]
        allo.each do |x|
            extra.push x.rank            
        end
        extra.sort!
                
    end
    
    def dit(text)
        if verbose
            puts "#{text}"
        end
    end
    
end

class AI_NoThanks
	attr_accessor :joueur
    
    PASSE   = Rules_NoThanks::PASSE
    PRENDRE = Rules_NoThanks::PRENDRE
    
	def initialize(joueur)
		joueur.nom="Roche"        
        @joueur = joueur                
	end
	
	def think( card, pot )
		if cash == 0
			puts "(Basic AI) Bankrupt."
			false
		end
	end
    
    def gotPrecedante?(card, tableau=@joueur.tableau)
        tableau.include?(card-1)
    end
    
    def gotSuivante?(card, tableau=@joueur.tableau)
        tableau.include?(card+1)
    end
    
    def isAdjacent?(card, tableau=@joueur.tableau)        
        gotPrecedante?(card, tableau) or gotSuivante?(card, tableau)        
    end
    
    def isHoleFiller?(card, tableau=@joueur.tableau)        
        gotPrecedante?(card, tableau) and gotSuivante?(card, tableau)
    end    
    
    def isHot?(card)
    #True si un autre joueur a la carte suivante ou precedante.
        
        @joueur.game.joueurs.each do |x|
            if x != @joueur and isAdjacent?(card, x.tableau)
                #puts "#{x.nom} got adjacent card."
                return true            
            end
        end
        
        false
    end
	
	def isAlive
        !isDead
		true
	end
	
	def isDead
        #true si les cartes precedante et suivante appartienne a d'autre joueurs.
		false
	end
	
	def isGood
	#carte avant et apres sont pas sorti	
	end
	
	def isAwesome
	#2 cartes avant et 2 cartes apres sont pas sorti
		
		if isGood
			#check pour les 2 autre  carte
			true
		end	
	end
    
    def isDisputed?(card)
        r=false
        if isAdjacent?(card) and isHot?(card)
            #puts "isDisputed = true"
            return true
        end
        
        return r
        
    end
end 

class AI_NoThanks_Bruno < AI_NoThanks
	attr_accessor :nom
	
	def initialize(joueur)
		super(joueur)
		joueur.nom="Bruno"
	end
	def think(card, pot, cardsLeft)
		
        if isDisputed?(card)
            joueur.dit "Conflit, je prend."
			PRENDRE
		elsif pot > card/4+2 and isAlive
			#joueur.dit "Je prend."
			PRENDRE
		elsif joueur.money == 0
			joueur.dit "Out of cash."
			PRENDRE
		else
			#joueur.dit "Passe."
			PASSE
        end
	end    
end

class AI_NoThanks_Patoo < AI_NoThanks
	attr_accessor :nom
	
	def initialize(joueur)
        super(joueur)
		joueur.nom="Patoo"
	end
    
	def think(card, pot, cardsLeft)
        #puts " #{pot} >  #{card}/2"
        
        if isDisputed?(card) 
            joueur.dit "Conflit, je prend."
			PRENDRE
            
        elsif isAdjacent?(card)
            joueur.dit "Youppi !!"
            PRENDRE
            
        elsif pot > card/2
            joueur.dit "Moi prendre !"
            PRENDRE
            
        elsif joueur.money == 0
            joueur.dit "no money!"
            PRENDRE
            
        else
            #joueur.dit "Passe."
            PASSE
        end
	end    
end

class AI_NoThanks_Colovini < AI_NoThanks
	attr_accessor :nom
	
    GREEDYNESS_LEVEL = 14
    
	def initialize(joueur)
        super(joueur)
		joueur.nom="Colovini"
	end
    
	def think(card, pot, cardsLeft)
        #puts " #{pot} >  #{card}/2"
       		
        if isDisputed?(card) 
            joueur.dit "Conflit, je prend."
			PRENDRE
        elsif isAdjacent?(card) and pot > 3
            joueur.dit "Free card!"
            PRENDRE                    
        elsif pot > (card/4+2)
            joueur.dit "... (prend la carte)"
            PRENDRE
        
        elsif joueur.money == 0
            joueur.dit "no money!"
            PRENDRE
        else
            #joueur.dit "Passe."
            PASSE
        end
	end
    
    
end

class AI_NoThanks_JP < AI_NoThanks
	attr_accessor :nom
	
    GREEDYNESS_LEVEL = 14
    
	def initialize(joueur)
        super(joueur)
		joueur.nom="JP"
	end
    
	def think(card, pot, cardsLeft)
        #puts " #{pot} >  #{card}/2"
       		
        if isDisputed?(card) 
            joueur.dit "Conflit, je prend."
			PRENDRE
            
        elsif isAdjacent?(card) and pot > 0
            joueur.dit "Free card!"
            PRENDRE                    
        
        elsif pot > (card/3+2)
            joueur.dit "... (prend la carte)"
            PRENDRE
        
        elsif !isAdjacent?(card) and isHot?(card) and pot != 0
            joueur.dit "... (je prend le hit.)"
            PRENDRE
        
        elsif joueur.money == 0
            joueur.dit "no money!"
            PRENDRE
        else
            #joueur.dit "Passe."
            PASSE
        end
	end
    
    
end

class AI_NoThanks_Feld < AI_NoThanks
#prend une carte juste quand ya pu une cenne ou que la carte vaut 0+
	attr_reader :nom, :cardsLeft
	
    LOWCASH = 6
    
	def initialize(joueur)
        super(joueur)
		joueur.nom="Stefan"
        
        @cardsLeft=nil
	end
	
	def think(card, pot, cardsLeft)
        
		#puts "(AI) carte: #{card}, valeur mise: #{value}, cash: #{cash}"
		if isDisputed?(card) 
            joueur.dit "Conflit, je prend."
			PRENDRE
            
        elsif isAdjacent?(card) and pot > (card * 0.30)
            joueur.dit "Schadenffreude!!!"
			PRENDRE
		
        elsif pot >= (card/2)
			joueur.dit  "<3 cette carte !"
			PRENDRE
       
        #Si low on cash then take a medium or lower card with some cash on it
        elsif joueur.money < LOWCASH and 
            ((card < 30 and pot > 6) or ( card <20 and pot > 4 ) or (card < 14 and pot != 0)) and
            cardsLeft > 3

			joueur.dit "(low on cash) Je prend ca !"
			PRENDRE
    		
        elsif joueur.money == 0
			joueur.dit "* Feld has Failed. no cash !! *"
			PRENDRE
		
		else        
			if isAdjacent?(card)
				joueur.dit "C'est juste bon pour moi on va faire tourner un peu.. [#{joueur.money}$] #{joueur.tableau}$"
			else
				joueur.dit "Non merci. v#{card-pot} [#{joueur.money}]$ #{joueur.tableau}"           
			end
			PASSE
		end
	end
	
	def myValue(card, pot)
        
		if isAdjacent(card)
			puts "*** est la !!"
			myValue -= card + 1
		#elsif tableau.include? card-1
		#	myValue = 0
		#elsif tableau.include? card+1
		#	myValue = -1
		else
			puts "pas la"
			myValue = card
		end
		myValue = 45
		#myValue -= pot
		joueur.dit "Pour moi cette carte vaut #{myValue} pts."
		
		return myValue
	end
	

end

class AI_NoThanks_Humain < AI_NoThanks
    attr_reader :nom, :cardsLeft
    
    def initialize(joueur)
        super(joueur)
        joueur.nom="Johnny"
        @cardsLeft=nil
    end
    
    def think(card, pot, cardsLeft)
     
        #puts "Voulez-vous la carte #{card} et #{pot}$  (#{joueur.money}$)"
        #choix = $stdin.gets
        #choix.strip!.upcase!
        
        choices = "yn"
            answer = ask("Voulez-vous la carte #{card} et #{pot}$  (#{joueur.money}$) [#{choices}]? ") do |q|
                       q.echo      = false
                       q.character = true
                       q.validate  = /\A[#{choices}]\Z/
                     end
        
        #puts "vous avez choisi : *#{choix}*"
        if answer == 'y' and joueur.money > 0
            #p "vous prenez la carte."
            PRENDRE
        
        else
            #p "Vous passez."
            PASSE
        end
    end    
    
end

class Joueur
    attr_accessor :ai,  :nom , :verbose, :winCount
    attr_reader   :tableau, :money, :game
  
	def initialize( rules, ai_init=AI_NoThanks_Basic, verbose=true, cash=11)
        @nom="John Doe"
        @game = rules
        @tableau = []		
        @ai = ai_init.new(self)
        @money = cash
        @verbose = verbose
        @winCount = 0
	end
	
    def seatPosition
        @game.getSeat(self)
    end
    
	def play(card, value, cardsLeft)        
		play = ai.think(card, value, cardsLeft)
        if play == game.class::PASSE
            @money -= 1
		    			
		else #prend        
            tableau.push card
			tableau.sort!
			@money += value			
        end        
        play
	end
	
	def score
		score = -money 
		derniereCarte=0
		
		tableau.each do |carte|
			if carte != derniereCarte+1
				score += carte
			end
			
			derniereCarte = carte
		end
		#@@logger.debug score
		
		return score
	end
    
    def dit(text)
        if verbose
            say "#{seatPosition}:(AI #{nom}) #{text}"
        end
    end
    
    def reset       
        @tableau.clear
        @money = 11        
    end
    
    #def to_s
    #    value = tableau
    #end
end



if __FILE__ == $0
    #choices = %w{jouer simulation quitter}
    system ("cls")
    say("\n+++++++ No Thanks +++++++")
    loop do
      choose do |menu|
        menu.layout = :menu_only

        menu.shell  = true

        menu.choice(:jouer, "Faire une partie.") do |command, details|
            choices = "345"
            answer = ask("Combien de joueurs ? [#{choices}]? ") do |q|
                       q.echo      = false
                       q.character = true
                       q.validate  = /\A[#{choices}]\Z/
                     end
            say("Your choice: #{answer}")

            say("Loading game:  #{details}...")
            game = Rules_NoThanks.new(answer.to_i).start(1) # nb de joueurs, nb de parties.        
        
        end
        
        menu.choice(:simulation, "Simule 1000 partie et retourne les stats.") do |command, details|
        
            choices = "345"
            answer = ask("Combien de joueurs ? [#{choices}]? ") do |q|
                       q.echo      = false
                       q.character = true
                       q.validate  = /\A[#{choices}]\Z/
                     end
                     
          say("Loading simulation..  #{details}...")
          game = Rules_NoThanks.new(answer.to_i).start(1000) # nb de joueurs, nb de parties.
          
        end
        menu.choice(:quitter, "Exit program.") { exit }
      end
    end
    
end

__END__

* TODO ***
- (done) faire la class JoueurHumain done. 9avril2013. 
- (done) fait fonctionner la simulation (n games).
- fonction isAlive.
- fonction isAwesome.
- je crois que player ne devrais pas connaitre "aucune" regles.

AI todo's (qui serait le fun, mais qui arrivera jamais prolly)

- receive hesitation time for since beginning of card, learn from it.
- approx money left.
- full counting money.

**********
class value prend un joueur et un carte et retourne la valeur brut de cette carte pour ce joueur.

**process**
ruleset envoi la carte au joueur.  joueur appel AI  AI fait le thinking 
